package model

import (
	"context"
	"database/sql"
	"fmt"
	"portal/internal/common"
	"portal/internal/types"

	"github.com/Masterminds/squirrel"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
)

var _ SysRoleModel = (*customSysRoleModel)(nil)

type (
	// SysRoleModel is an interface to be customized, add more methods here,
	// and implement the added methods in customSysRoleModel.
	SysRoleModel interface {
		sysRoleModel
		withSession(session sqlx.Session) SysRoleModel
		InsertGetId(ctx context.Context, session sqlx.Session, data *SysRole) (int64, error)
		GetListByUserId(ctx context.Context, session sqlx.Session, userId int64) ([]*SysRole, error)
		GetListByCondition(ctx context.Context, session sqlx.Session, userId int64, cond *types.ListRoleRequest) ([]*SysRole, error)
		DelRoleMenus(ctx context.Context, session sqlx.Session, roleId int64, menuIds []*int64) error
		AddRoleMenus(ctx context.Context, session sqlx.Session, roleId int64, menuIds []*int64) error
		DelRoleDepts(ctx context.Context, session sqlx.Session, roleId int64, deptIds []*int64) error
		AddRoleDepts(ctx context.Context, session sqlx.Session, roleId int64, deptIds []*int64) error
		ListRoleMenus(ctx context.Context, session sqlx.Session, roleId int64) ([]*SysRoleMenu, error)
		ListRoleDepts(ctx context.Context, session sqlx.Session, roleId int64) ([]*SysRoleDept, error)
		DelRMByRid(ctx context.Context, session sqlx.Session, roleId int64) error
		DelRDByRid(ctx context.Context, session sqlx.Session, roleId int64) error
		DelRole(ctx context.Context, session sqlx.Session, roleId int64) error
		Edit(ctx context.Context, session sqlx.Session, data *SysRole) error
	}

	customSysRoleModel struct {
		*defaultSysRoleModel
	}
)

// NewSysRoleModel returns a model for the database table.
func NewSysRoleModel(conn sqlx.SqlConn) SysRoleModel {
	return &customSysRoleModel{
		defaultSysRoleModel: newSysRoleModel(conn),
	}
}

func (m *customSysRoleModel) withSession(session sqlx.Session) SysRoleModel {
	return NewSysRoleModel(sqlx.NewSqlConnFromSession(session))
}

func (m *customSysRoleModel) GetListByUserId(ctx context.Context, session sqlx.Session, userId int64) ([]*SysRole, error) {
	sb := squirrel.Select("role.*").
		From("sys_user_role ur").
		Where(squirrel.Eq{"user_id": userId}).
		InnerJoin("sys_role role on role.role_id = ur.role_id and role.del_flag = ?", common.EXIST_FLAG)
	query, args, err := sb.ToSql()
	if err != nil {
		return nil, err
	}
	var roles []*SysRole
	if session != nil {
		err = session.QueryRowsPartialCtx(ctx, &roles, query, args...)
	} else {
		err = m.conn.QueryRowsPartialCtx(ctx, &roles, query, args...)
	}
	return roles, err
}

func (m *customSysRoleModel) GetListByCondition(ctx context.Context, session sqlx.Session, userId int64, cond *types.ListRoleRequest) ([]*SysRole, error) {
	sb := squirrel.Select("role.*").
		From("sys_user_role ur").
		Where(squirrel.Eq{"user_id": userId})
	innerJoin := fmt.Sprintf("sys_role role on role.role_id = ur.role_id and role.del_flag = %s", common.EXIST_FLAG)
	if len(cond.RoleName) > 0 {
		innerJoin += fmt.Sprintf(" and role.role_name = %s", cond.RoleName)
	}
	if len(cond.RoleCode) > 0 {
		innerJoin += fmt.Sprintf(" and role.role_code = %s", cond.RoleCode)
	}
	if len(cond.Status) > 0 {
		innerJoin += fmt.Sprintf(" and role.status = %s", cond.Status)
	}
	if cond.BeginTime != 0 {
		innerJoin += fmt.Sprintf(" and role.update_time > %d", cond.BeginTime)
	}
	if cond.EndTime != 0 {
		innerJoin += fmt.Sprintf(" and role.update_time < %d", cond.EndTime)
	}
	sb = sb.InnerJoin(innerJoin)
	sb = sb.Offset(uint64((cond.PageNum - 1) * (cond.PageSize))).Limit(uint64(cond.PageSize))
	query, args, err := sb.ToSql()
	if err != nil {
		return nil, err
	}
	var roles []*SysRole
	if session != nil {
		err = session.QueryRowsPartialCtx(ctx, &roles, query, args...)
	} else {
		err = m.conn.QueryRowsPartialCtx(ctx, &roles, query, args...)
	}
	return roles, err
}

func (m *customSysRoleModel) DelRoleDepts(ctx context.Context, session sqlx.Session, roleId int64, deptIds []*int64) error {
	db := squirrel.Delete("sys_role_dept").
		Where(squirrel.Eq{"role_id": roleId}).
		Where(squirrel.Eq{"dept_id": deptIds})
	query, args, err := db.ToSql()
	if err != nil {
		return err
	}
	if session != nil {
		_, err = session.ExecCtx(ctx, query, args...)
	} else {
		_, err = m.conn.ExecCtx(ctx, query, args...)
	}
	return err
}

func (m *customSysRoleModel) AddRoleDepts(ctx context.Context, session sqlx.Session, roleId int64, deptIds []*int64) error {
	ib := squirrel.Insert("").Into("sys_role_dept").
		Columns("role_id", "dept_id")
	for _, val := range deptIds {
		ib = ib.Values(roleId, val)
	}
	query, args, err := ib.ToSql()
	if err != nil {
		return err
	}
	if session != nil {
		_, err = session.ExecCtx(ctx, query, args...)
	} else {
		_, err = m.conn.ExecCtx(ctx, query, args...)
	}
	return err
}

func (m *customSysRoleModel) DelRoleMenus(ctx context.Context, session sqlx.Session, roleId int64, menuIds []*int64) error {
	db := squirrel.Delete("sys_role_menu").
		Where(squirrel.Eq{"role_id": roleId}).
		Where(squirrel.Eq{"menu_id": menuIds})
	query, args, err := db.ToSql()
	if err != nil {
		return err
	}
	if session != nil {
		_, err = session.ExecCtx(ctx, query, args...)
	} else {
		_, err = m.conn.ExecCtx(ctx, query, args...)
	}
	return err
}

func (m *customSysRoleModel) AddRoleMenus(ctx context.Context, session sqlx.Session, roleId int64, menuIds []*int64) error {
	ib := squirrel.Insert("").Into("sys_role_menu").
		Columns("role_id", "menu_id")
	for _, val := range menuIds {
		ib = ib.Values(roleId, val)
	}
	query, args, err := ib.ToSql()
	if err != nil {
		return err
	}
	if session != nil {
		_, err = session.ExecCtx(ctx, query, args...)
	} else {
		_, err = m.conn.ExecCtx(ctx, query, args...)
	}
	return err
}

func (m *customSysRoleModel) ListRoleDepts(ctx context.Context, session sqlx.Session, roleId int64) ([]*SysRoleDept, error) {
	sb := squirrel.Select("*").
		From("sys_role_dept").
		Where(squirrel.Eq{"role_id": roleId})
	query, args, err := sb.ToSql()
	if err != nil {
		return nil, err
	}
	var rds []*SysRoleDept
	if session != nil {
		err = session.QueryRowCtx(ctx, &rds, query, args...)
	} else {
		err = m.conn.QueryRowCtx(ctx, &rds, query, args...)
	}
	return rds, err
}

func (m *customSysRoleModel) ListRoleMenus(ctx context.Context, session sqlx.Session, roleId int64) ([]*SysRoleMenu, error) {
	sb := squirrel.Select("*").
		From("sys_role_menu").
		Where(squirrel.Eq{"role_id": roleId})
	query, args, err := sb.ToSql()
	if err != nil {
		return nil, err
	}
	var rms []*SysRoleMenu
	if session != nil {
		err = session.QueryRowCtx(ctx, &rms, query, args...)
	} else {
		err = m.conn.QueryRowCtx(ctx, &rms, query, args...)
	}
	return rms, err
}

func (m *customSysRoleModel) DelRDByRid(ctx context.Context, session sqlx.Session, roleId int64) error {
	db := squirrel.Delete("sys_role_dept rd").
		Where(squirrel.Eq{"role_id": roleId})
	query, args, err := db.ToSql()
	if err != nil {
		return err
	}
	if session != nil {
		_, err = session.ExecCtx(ctx, query, args...)
	} else {
		_, err = m.conn.ExecCtx(ctx, query, args...)
	}
	return err
}

func (m *customSysRoleModel) DelRMByRid(ctx context.Context, session sqlx.Session, roleId int64) error {
	db := squirrel.Delete("sys_role_menu rm").
		Where(squirrel.Eq{"role_id": roleId})
	query, args, err := db.ToSql()
	if err != nil {
		return err
	}
	if session != nil {
		_, err = session.ExecCtx(ctx, query, args...)
	} else {
		_, err = m.conn.ExecCtx(ctx, query, args...)
	}
	return err
}

func (m *customSysRoleModel) DelRole(ctx context.Context, session sqlx.Session, roleId int64) error {
	ub := squirrel.Update(m.table).
		Set("del_flag", common.DELETED_FLAG).
		Where(squirrel.Eq{"role_id": roleId})
	query, args, err := ub.ToSql()
	if err != nil {
		return err
	}
	if session != nil {
		_, err = session.ExecCtx(ctx, query, args...)
	} else {
		_, err = m.conn.ExecCtx(ctx, query, args...)
	}
	return err
}

func (m *customSysRoleModel) InsertGetId(ctx context.Context, session sqlx.Session, data *SysRole) (int64, error) {
	query, args, err := squirrel.Insert(m.table).Columns(sysRoleInsertRows).
		Values(data.RoleName, data.RoleKey, data.RoleSort, data.DataScope, data.Status, data.DelFlag, data.CreateBy, data.CreateTime, data.UpdateBy, data.UpdateTime, data.Remark).
		ToSql()
	if err != nil {
		return 0, err
	}
	var sqlRes sql.Result
	if session != nil {
		sqlRes, err = session.ExecCtx(ctx, query, args...)
	} else {
		sqlRes, err = m.conn.ExecCtx(ctx, query, args...)
	}
	if err != nil {
		return 0, err
	}
	id, err := sqlRes.LastInsertId()
	return id, err
}

func (m *customSysRoleModel) Edit(ctx context.Context, session sqlx.Session, data *SysRole) error {

	query, args, err := squirrel.Update(m.table).
		SetMap(
			squirrel.Eq{
				"role_name":   data.RoleName,
				"role_sort":   data.RoleSort,
				"role_key":    data.RoleKey,
				"data_scope":  data.DataScope,
				"status":      data.Status,
				"del_flag":    data.DelFlag,
				"update_time": data.UpdateTime,
				"update_by":   data.UpdateBy,
			}).
		Where(squirrel.Eq{"role_id": data.RoleId}).
		ToSql()
	if err != nil {
		return err
	}
	if session != nil {
		_, err = session.ExecCtx(ctx, query, args...)
	} else {
		_, err = m.conn.ExecCtx(ctx, query, args...)
	}
	return err
}
